home *** CD-ROM | disk | FTP | other *** search
- title POP-CAL - popup calendar for 1583 - 9999 AD.
- comment ~
- Use Alt-C, to pop up and terminate calendar generator.
- Use left/right cursor keys to change months.
- Use Up/Down cursor keys to change years.
- ~
- cseg segment
- assume cs:cseg
- ;-----------------------------------------------------------------------------
- ppfdata struc ;Variable data is stored in PPF area.
- rom16h dw ?,? ;Holds old Interrupt 16h BIOS vector.
- savss dw ? ;Holds caller's stack segment
- savsp dw ? ;Holds caller's stack pointer
- video_seg dw ? ;Holds base segment of video buffer
- mnth db ? ;calendar's month
- year dw ? ;calendar's year
- weekday db ? ;1st day of month 0=Sun,,,,6=Sat
- left dw ? ;Offset to left edge of calendar, row 0
- hilite db ? ;Calendar display attribute.
- busy db ? ;Busy flag, for our stack
- crt_mode db ? ;Holds current crt mode
- crt_cols dw ? ;Number of columns on screen
- status_port dw ? ;video card status port address
- ppfdata ends
- org 100h-status_port
- v ppfdata <> ;Locate ppfdata and assign prefix "v".
- ;-----------------------------------------------------------------------------
- org 100h
- begin: jmp Install ;Initialize calendar; go resident.
- db "Copyright 1986 Ziff-Davis Publishing Co.",1Ah
- days db 'SunMonTueWedThrFriSat'
- months db 'JanFebMarAprMayJunJulAugSepOctNovDec'
- numdays db 31,28,31,30,31,30,31,31,30,31,30,31 ;Jan-Dec days/month
- bios dw 40h ;Points to BIOS data segment
- scan_code dw 2e00h ;(Alt)(C) scan code to start/stop program.
- ;---------------- Intercept keystroke reading --------------------------------
- int16h: sti ;Turn interrupts back on.
- cmp ah,0 ;See if was request for an actual key.
- je come_back ;If so, we'll check it first.
- skip_us:jmp dword ptr v.rom16h ;Otherwise, let go straight to caller.
- come_back:
- cmp v.busy,0 ;Is calendar routine busy?
- jne skip_us ;If so, pass all keys along.
- getkey: pushf ;Else, call BIOS keyboard routine,
- call dword ptr v.rom16h ;so it will return the key to us.
- cmp ax,scan_code ;Is scan code correct?
- je now_busy ;If so, go to work.
- iret ;Else just pass on key to caller.
- now_busy:
- mov v.busy,1 ;Set busy flag, to protect our stack.
- mov v.savss,ss ;Save caller's stack segment.
- mov v.savsp,sp ;save caller's stack pointer.
- mov sp,cs
- cli ;Avoid interrupt right now.
- mov ss,sp ;Reset stack to our code segment.
- mov sp,offset v.rom16h ;Start our stack below ROM16 addr.
- sti
- push ax
- push bx
- push cx
- push dx
- push bp
- push si ;Save all user's registers.
- push di
- push ds
- push es
- call begin_calendars ;Go do the calendars.
- pop es ;When finished,...
- pop ds
- pop di
- pop si
- pop bp ;Restore all user's registers
- pop dx
- pop cx
- pop bx
- pop ax
- cli
- mov ss,v.savss ;restore old stack
- mov sp,v.savsp
- sti
- mov ah,0 ;Reset AH to BIOS key read function.
- mov v.busy,0 ;Allow reuse of calendar stack.
- jmp getkey ;Go get new key from BIOS.
- ;--------------- Read keys input. Process calendars. ----------------------
- begin_calendars:
- push cs
- push cs
- pop ds ;Set DS and ES to our code segment.
- pop es
- cld ;Set for standard "up" direction.
- assume ds:cseg ;Let Assembler use DS addressing, now.
- call wndo ;Save Screen data. Put old calendar on it.
- jmp showcal ;Go fill in calendar, in case none made yet.
- nxtcal: mov v.mnth,dh ;Store new month value.
- mov v.year,cx ;Store new year.
- showcal:call fill ;Format and display new calendar.
- input: mov ah,0 ;Wait for key stroke.
- int 16h
- cmp ax,scan_code ;Another (Alt)(C)?
- jne getdate ;If not, initialize CX (year) and DH (month).
- exit: call wndo ;Restore original screen data and
- ret ; go restore caller's stack... all done.
- getdate:mov dh,v.mnth ;Load calendar month into DH.
- mov cx,v.year ;Load calendar year into CX.
- trylft: cmp ah,75 ;Was key a left arrow?
- jne tryrgt ;If not, go see if was a right arrow.
- dec dh ; If a left arrow, reduce month by 1.
- jns nxtcal ; When pass Jan, switch
- mov dh,11 ; to Dec of prior year.
- dec cx ; Reduce year by one.
- jmp chkloyr ; Go check that its still in range.
- tryrgt: cmp ah,77 ;Right arrow? (increase month)
- jne tryup ;If not, see if an Up arrow.
- inc dh ; Was right arrow, so increase month.
- cmp dh,11 ; See if past December.
- jng nxtcal ; If not, go make the calendar.
- mov dh,0 ; If was, reset month to January
- inc cx ; and move up to next year.
- jmp chkhiyr ; Then insure year is still in range.
- tryup: cmp ah,72 ;Up arrow? (increase year)
- jne trydwn ;If not, see if was a down key.
- inc cx ; If Up, increase year by 1.
- chkhiyr:cmp cx,10000 ; Year can only be 4 digits.
- jge input ; Ignore key, if date goes out of range.
- jmp nxtcal ; Otherwise go make new calendar.
- trydwn: cmp ah,80 ;Down arrow? (decrease year)
- jne input ;If not, ignore key and go get new one.
- dec cx ; Down key reduces year by 1.
- chkloyr:cmp cx,1582 ;Calendar calculations only good for 11/1582
- jle input ; on, so ignore key, if year now < 1583.
- jmp nxtcal ; Otherwise, go make new calendar.
- ;------------------ Fill in the new calendar ------------------------------
- fill: call firstday ;Calculate weekday of 1st of month.
- mov es,v.video_seg ;Point ES to write to screen.
- mov al,v.mnth ;Put current month into AL.
- cbw ;Zeros out AH.
- mov cx,3 ;Zero out CH. Put 3 in CL.
- mul cl ;AX = # bytes to current month's name
- mov si,offset months; from start of "months".
- add si,ax ;SI=offset to month's name
- mov di,v.crt_cols ;Put screen width (# columns) in DI.
- add di,v.left ;Move to left edge, second row.
- add di,22 ;Move on to center month in the row.
- movname:lodsb ;Load character from month's name.
- call writebyte ;Output char of month's name to screen.
- loop movname ;Do three times, since cx=3.
- add di,2 ;Skip a space, after the month's name.
- mov ax,v.year ;Load year into AX.
- mov bl,100 ;Will use to split year into centuries
- div bl ; portion and remaining years in century.
- mov bh,1 ;Set flag, so will retain leading zero.
- call unpack ;Display the centuries portion of year.
- mov al,ah ;Put remainder in al.
- call unpack ;Display remaining 2 digits in year.
- setdi: mov ax,v.crt_cols ;Load current screen width.
- mov bl,4 ;Mult by # rows to skip.
- mul bl ;Calculate offset to 4th row of calendar.
- add ax,4 ;Skip in 2 columns on that row.
- mov di,v.left ;Set to offset of top left corner of calendar.
- add di,ax ;Move to location of 1st date on screen.
- mov dl,v.weekday ;Retrieve week day of 1st of cal's month.
- mov cl,dl ;Store as count of days to blank in 1st week.
- mov dh,6 ;DH = # weeks left to fill in.
- jcxz start_current_month ;If Sunday, CX = 0 and no blanking needed.
- blnkrw1:call blank2 ;Blank out 1st part of 1st week.
- loop blnkrw1
- start_current_month:
- mov cl,7 ;Number of days to display in 1st week
- sub cl,dl ; = 7 - weekday of 1st day.
- mov bl,v.mnth ;Set index for days-per-month array.
- xor bh,bh ;Set flag to suppress leading zeros.
- mov dl,numdays[bx] ;Get # days in current month.
- mov al,1 ;Set AL to 1st day of month.
- showdays:
- call unpack ;Format and display the date value.
- inc al ;Increment to next day.
- add di,4 ;Skip to next day position.
- dec dl ;Reduce # days left in month.
- jz blnkrst ;If last day, blank rest of the 6 weeks.
- loop showdays ;Otherwise continue displaying dates in week.
- call nxtweek ;When reach end of a week, space to next one.
- jmp showdays ; and continue on new row of calendar.
- blnkrw4:call blank2 ;Blank out remaining days in week.
- blnkrst:loop blnkrw4 ;Blank to end of current week.
- call nxtweek ;Relocate DI to 1st day of next week.
- jnz blnkrw4 ;If not past last week, go blank week out.
- push cs ;When all done, restore ES to CS.
- pop es
- ret ;Return to keyboard reading routine.
- ;---------------------- Move DI to 1st date in next week ---------------------
- nxtweek:add di,v.left ;Move to left edge of next row of calendar.
- add di,4 ;Move in an additional 2 columns.
- mov cl,7 ;Restart week count.
- dec dh ;Reduce weeks left in calendar.
- ret
- ;---------------------- Compute weekday of 1st day of month ------------------
- firstday:
- mov si,v.year ;Hold year in SI, of quick access.
- mov numdays[1],28 ;Assume this isn't a leap year.
- test si,3 ;Now see if year divisible by 4.
- jnz getday ;If not, was actually not a leap year.
- feb29: mov numdays[1],29 ;Else was leap year with 29 days.
- getday: mov bx,6 ;BX=Saturday,(weekday of 1/1/1583)
- mov ax,si ;Calendar year to AX.
- sub ax,1583 ;Calc. # years since our base year.
- add bx,ax ;Calendar advances 1 weekday per
- ; year unless leap years intervene.
- add ax,2 ;Adj diff, so even #, if the calendar
- mov cx,4 ; year was year after a leap year.
- cwd ;Zero out DX.
- div cx ;Calculate # leap years before this.
- add bx,ax ;Add 1 day to week, for each leap yr.
- cmp si,1700 ;Things work normally 'til 1700, which
- jl cnvrt ;not a leap year, per Gregorian cal.
- mov ax,si ;Year back to AX, again.
- sub ax,1600 ;Get # years since 1600.
- mov cl,100
- cwd ;Convert to number of centuries.
- div cx
- cmp dx,0 ;If remainder=0 this is centennial yr.
- jne deduct
- test ax,3 ;If century is evenly divisible by
- jz decax ; 400, it is also still a leap year.
- mov numdays[1],28 ;Other centenial years have 28 days.
- decax: dec ax ;If centenial yr, sub 1 from centuries
- deduct: sub bx,ax ;Subtract 1 weekday per century.
- cwd
- mov cl,4
- div cx ;Calculate centuries mod 400.
- add bx,ax ;Add back 1 day per 400 years.
- cnvrt: mov si,offset numdays ;Add in the days per month, this yr.
- xor ah,ah ;First clear hi byte of accumulator
- mov cl,v.mnth ;Load cx with month counter
- jcxz final ;If January, are ready for final calc.
- addmnth:lodsb ;Get low byte into accumulator.
- add bx,ax ;Add to days count.
- loop addmnth ;Continue until prior months added in.
- final: mov ax,bx ;AX=# days advanced since 1/1/1583.
- cwd ;Clear out DX.
- mov cl,7 ;Find # full weeks since 1583.
- div cx ;DL=weekday of 1st of calendar's month
- mov v.weekday,dl ;Save results of this work.
- ret
- ;-------------- Convert binary # in AL to ASCII and output to ES:DI ----------
- unpack: push ax ;(AL should binary # from 0-99.)
- push bx
- aam ;Divide al by 10.
- add ax,3030h ;Convert digits to ASCII.
- cmp ah,30h ;See if 10's digit was a zero.
- jne putnum ;If not, go output it to screen.
- cmp bh,1 ;If BH=1, leading zero is ok, so
- je putnum ; go write it. Otherwise, replace
- mov ah,20h ; leading zero with a blank.
- putnum: mov bl,al ;Need to write high order digit first,
- mov al,ah ; so save low digit and move hi into AL.
- call writebyte ;Output 10's digit to screen.
- mov al,bl ;Put 1's digit back in AL.
- call writebyte ;Write 1's digit.
- pop bx ;Restore BX.
- pop ax ;Retrieve old binary value.
- ret ;All done.
- ;------------------- Blank out 2 digit calendar date field. Update DI. ------
- blank2: mov al,20h ;Put a blank in AL
- call writebyte ;Write a blank over 1st digit.
- call writebyte ;Write a blank over 2nd digit.
- add di,4 ;Skip 2 spaces, to next date.
- ret
- ;------------------- Write char AL on screen at ES:DI. Update DI. ------------
- writebyte:
- push dx ;Save DX.
- mov dx,v.status_port ;Reset to video status port address.
- mov ah,al ;Move character to AH, for now.
- on1: in al,dx ;Wait until not doing a
- test al,1 ; horizontal scan retrace.
- jnz on1
- cli
- on2: in al,dx ;Now wait until next horizontal
- test al,1 ; retrace begins, so KNOW will have
- jz on2 ; entire retrace time period to work.
- mov al,ah ;Retrieve character from AH.
- stosb ;Store character on screen.
- sti
- inc di ;Skip over attribute byte on screen.
- pop dx ;Restore old DX value.
- ret
- ;---------------------- Switch memory field with screen data -----------------
- wndo: call getprms ;Get current video parms
- mov dx,v.status_port ;Point DX to Status Port.
- sub dx,2 ;Back up to control port.
- mov es,bios ;Point to ROM BIOS data segment
- mov bh,es:[65h] ;Get current setting of video card.
- mov al,bh ;Move to AL, for output, after reset.
- and al,0f7h ;Turn off video enable bit only.
- out dx,al ;Turn off screen.
- mov si,offset data_strt ;Point to calendar data in memory.
- mov di,v.left ;Set DI=upper left corner of calendar.
- mov es,v.video_seg ;Set ES to video buffer segment.
- mov bl,11 ;11 rows in the calendar.
- nxtlin: mov cx,30 ;There are 30 words per calendar row.
- getchr: mov ax,es:[di] ;Get next word from screen buffer.
- movsw ;Move data word to screen.
- mov [si-2],ax ;Store screen character in our data.
- loop getchr ;Continue across row.
- add di,v.left ;Move to start of next calendar row.
- dec bl ;Reduce rows-to-go count.
- jnz nxtlin ;Do next row, if more to go.
- mov al,bh ;Restore video setting to original
- out dx,al ; and turn on screen again.
- push cs
- pop es ;Restore ES to our code segment.
- ret
- ;------------------- Get video display parameters ----------------------------
- getprms:push es
- mov es,bios ;Point ES to BIOS data segment.
- mov ax,es:[4ah] ;Put current column width.
- shl ax,1 ;Multiply by 2, for attribute bytes.
- mov v.crt_cols,ax ;Store in our data, for easy access.
- sub al,30*2 ;Back off from right side, by width of
- mov v.left,ax ;calendar. Equals offset to left edge.
- mov ax,es:[63h] ;Get base address of video card.
- add ax,6 ;Calculate status port address and
- mov v.status_port,ax ; save for checking horiz scans.
- mov v.hilite,70h ;Black on white background.
- mov bl,es:[49h] ;Get video mode into BL.
- cmp bl,7 ;In Monochrome video mode?
- mov ax,0B000h ;Monochrome buffer seg at 0B000h.
- je setseg ;If monochrome, go store video seg.
- mov ax,0B800h ;If graphics card, video seg=0B800h.
- test bl,1 ;1,3 are color text on graphics card.
- jz setseg
- mov v.hilite,1fh ;Use bright white on blue, if color.
- setseg: mov v.video_seg,ax ;Store video_segment value
- pop es ;Restore Es.
- ret ;Done.
- ;------------------ Create calendar form and become resident. ---------------
- Install:call getprms ;Call, to get correct color attribute.
- mov di,offset data_strt ;Point to end of our code segment.
- mov ah,v.hilite ;Set attribute byte in AH.
- mov al,20h ;Will store blanks in calendar area.
- mov cx,11*30 ;11 rows of 30 columns each.
- rep stosw ;Blank out calendar work area
- push di ;Save end of calendar offset.
- mov si,offset days ;Now store day's names in calendar.
- mov bl,7 ;Set names-to-go counter to 7.
- mov di,offset data_strt ;Point to start of calendar
- add di,3*30*2+4 ;Skip in 3 rows + 2 columns.
- movday: mov cx,3 ;There are 3 chars per name.
- movchr: movsb ;Transfer character into calendar.
- inc di ;Skip over attribute byte.
- loop movchr ;Continue for three characters.
- inc di
- inc di ;Add 2 to DI, to skip space.
- dec bl ;Reduce names-to-go count.
- jnz movday ;Continue until all stored.
- mov ah,2ah ;Get current date from DOS.
- int 21h
- dec dh ;Convert months,so Jan=0, etc.
- mov v.mnth,dh ;Save month and year values.
- mov v.year,cx
- go_resident:
- xor ax,ax
- mov es,ax ;Set ES to 0.
- mov ax,es:[16h*4]
- mov v.rom16h,ax ;Store rom interrupt 16h address.
- mov ax,es:[16h*4+2]
- mov v.rom16h+2,ax
- mov ax,offset int16h
- cli
- mov es:[16h*4],ax ;Point Interrupt 16 to our code.
- mov es:[16h*4+2],cs
- sti
- pop dx ;Retrieve address of end of data.
- int 27h ;Now become resident.
- ;----------------- Calendar data area --------------------------------
- data_strt equ this word
- cseg ends
- end begin